
package com.hlkj.pay.app.merchant.ext.impl.wxpay;
/*
 * Hlpay-Plus aggregate payment system.
 * Copyright (c) 2024-2025 Hlpay Team Copyright has the right of final interpretation.
 */

import java.security.PrivateKey;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.springframework.stereotype.Repository;

import com.hlkj.framework.common.pojo.CommonResult;
import com.hlkj.framework.common.util.json.JsonUtils;
import com.hlkj.framework.extension.annotation.Extension;
import com.hlkj.pay.app.merchant.ext.ChannelMerchantRiskExtPt;
import com.hlkj.pay.app.merchant.ext.MerchantExtConstant;
import com.hlkj.pay.app.merchant.ext.dto.order.ChannelRiskOrderContext;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.constant.ComplaintStatusType;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.constant.WxPayExtConstant;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.dto.*;
import com.hlkj.pay.app.merchant.ext.impl.wxpay.handler.WxPayCommonParamsHandle;
import com.hlkj.pay.common.MerchantResultCode;
import com.hlkj.pay.dto.order.notify.OrderRiskNotifyDto;
import com.hlkj.pay.infrastructure.model.order.OrderComplaintChannelDO;
import com.hlkj.pay.infrastructure.model.order.OrderComplaintDO;
import com.hlkj.pay.infrastructure.model.order.OrderRiskDO;
import com.hlkj.pay.infrastructure.model.order.PayOrderDO;
import com.hlkj.pay.service.order.IPayOrderService;
import com.hlkj.pay.util.DateUtils;
import com.hlkj.pay.util.HttpRequestUtil;
import com.hlkj.pay.util.MerchantUtils;
import com.ijpay.core.IJPayHttpResponse;
import com.ijpay.core.enums.AuthTypeEnum;
import com.ijpay.core.enums.RequestMethodEnum;
import com.ijpay.core.kit.PayKit;
import com.ijpay.core.kit.WxPayKit;
import com.ijpay.wxpay.WxPayApi;
import com.ijpay.wxpay.enums.WxDomainEnum;
import com.ijpay.wxpay.enums.v3.ComplaintsApiEnum;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * 微信订单投诉操作
 * 
 * @author HlpayTeam
 * @date 2024/10/10 13:51
 */
@Slf4j
@Repository
@RequiredArgsConstructor
@Extension(bizId = MerchantExtConstant.CHANNEL_WX_PAY)
public class WxMerchantRiskExtPt implements ChannelMerchantRiskExtPt {

    private final WxPayCommonParamsHandle wxPayCommonParamsHandle;

    private final IPayOrderService payOrderService;

    @Override
    public CommonResult<OrderRiskNotifyDto> riskStatusParse(HttpServletRequest request, ChannelRiskOrderContext channelRiskOrderContext) {
        CommonResult<OrderRiskNotifyDto> orderRiskNotifyDtoCommonResult = CommonResult.success();
        try {

            CommonResult<WxPayConfig> wxPayConfigCommonResult = wxPayCommonParamsHandle.wxComplaintConfig(channelRiskOrderContext.getOrderComplaintChannelDO(),
                    channelRiskOrderContext.isTest());
            if (!wxPayConfigCommonResult.isSuccess()) {
                channelRiskOrderContext.setRemark(wxPayConfigCommonResult.getMsg());
                channelRiskOrderContext.setProcessStatus(false);
            }
            WxPayConfig wxPayConfig = wxPayConfigCommonResult.getData();

            String timestamp = request.getHeader("Wechatpay-Timestamp");
            String nonce = request.getHeader("Wechatpay-Nonce");
            String serialNo = request.getHeader("Wechatpay-Serial");
            String signature = request.getHeader("Wechatpay-Signature");
            log.info("timestamp:{} nonce:{} serialNo:{} signature:{}", timestamp, nonce, serialNo, signature);
            String result = HttpRequestUtil.requestBodyParams(request);
            log.info("微信风险通知密文 {}", result);
            // String plainText = "{\"out_trade_no\":\"9965651481974950387712\",\"complaint_time\":\"2025-03-03T11:00:00+08:00\",\"amount\":12300,\"payer_phone\":\"13567230776\",\"complaint_detail\":\"我充值后还是用不了VPN\",\"complaint_state\":\"PAYER_COMPLAINTED\",\"transaction_id\":\"4200002519202503036857074987\",\"sub_mchid\":\"746798399\",\"complaint_handle_state\":\"MERCHANT_FULL_REFUNDED\",\"action_type\":\"MERCHANT_RESPONSE\",\"complaint_id\":\"200000020250303110255410899\"}";
            String plainText = null;
            // 微信公钥验证签名并解密
            if(serialNo.equals(wxPayConfig.getPayPublicId())){
                plainText = WxPayKit.verifyPublicKeyNotify(result, signature, nonce, timestamp,wxPayConfig.getApiV3Key(), wxPayConfig.getPayPublicPath());
            }else if(serialNo.equals(wxPayCommonParamsHandle.getPlatSerialNumber(wxPayConfig))){
                plainText = WxPayKit.verifyNotify(serialNo,result, signature, nonce, timestamp,wxPayConfig.getApiV3Key(), wxPayConfig.getPlatformCertPath());
            }
            log.info("微信风险通知明文 {}", plainText);

            if (StrUtil.isEmpty(plainText)) {
                channelRiskOrderContext.setProcessStatus(false);
                log.info("微信风险回调验签不通过,signature:{}", signature);
                orderRiskNotifyDtoCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
            }
            OrderRiskNotifyDto orderRiskNotifyDto = new OrderRiskNotifyDto();
            // 回调映射
            WxRiskNotifyResp notifyResp = JsonUtils.parseObject(plainText, WxRiskNotifyResp.class);
            // 数据回写
            OrderRiskDO updateOrderRiskDO = new OrderRiskDO();
            updateOrderRiskDO.setRiskId(notifyResp.getRecord_id());
            updateOrderRiskDO.setRiskType(notifyResp.getRisk_type());
            updateOrderRiskDO.setRiskDescription(notifyResp.getRisk_description());
            updateOrderRiskDO.setCompanyName(notifyResp.getCompany_name());
            updateOrderRiskDO.setAppId(wxPayConfig.getMchId());
            updateOrderRiskDO.setPunishPlan(notifyResp.getPunish_plan());
            updateOrderRiskDO.setPunishDescription(notifyResp.getPunish_description());
            updateOrderRiskDO.setSubMchid(notifyResp.getSub_mchid());
            if(StrUtil.isNotEmpty(notifyResp.getPunish_time())){
                updateOrderRiskDO.setPunishTime(DateUtils.shanghaiDate(notifyResp.getPunish_time(), DateTimeFormatter.ISO_OFFSET_DATE_TIME));
            }
            orderRiskNotifyDto.setOrderRiskDO(updateOrderRiskDO);
            orderRiskNotifyDtoCommonResult.setData(orderRiskNotifyDto);
        }
        catch (Exception e) {
            channelRiskOrderContext.setProcessStatus(false);
            orderRiskNotifyDtoCommonResult.setCode(MerchantResultCode.MERCHANT_CHANNEL_CALLBACK_SIGN_ERROR.getCode());
        }
        log.info("微信风险通知回调,解析后的订单数据为 payOrderDO:{}", JsonUtils.toJsonStringNotNull(orderRiskNotifyDtoCommonResult.getData()));
        return orderRiskNotifyDtoCommonResult;


    }

    @Override
    public CommonResult<String> queryComplaintNotify(ChannelRiskOrderContext channelComplaintOrderContext) {
        OrderComplaintChannelDO orderComplaintChannelDO = channelComplaintOrderContext.getOrderComplaintChannelDO();
        CommonResult<WxPayConfig> wxPayConfigCommonResult = wxPayCommonParamsHandle.wxComplaintConfig(orderComplaintChannelDO,
                channelComplaintOrderContext.isTest());
        if (!wxPayConfigCommonResult.isSuccess()) {
            channelComplaintOrderContext.setRemark(wxPayConfigCommonResult.getMsg());
            channelComplaintOrderContext.setProcessStatus(false);
        }
        WxPayConfig wxPayConfig = wxPayConfigCommonResult.getData();

        try {

            IJPayHttpResponse response = WxPayApi.v3(
                    RequestMethodEnum.GET,
                    WxDomainEnum.CHINA.toString(),
                    "/v3/merchant-risk-manage/violation-notifications",
                    wxPayConfig.getMchId(),
                    wxPayConfig.getSerialNo(),
                    null,
                    wxPayConfig.getPrivateKeyPath(),
                    "",
                    AuthTypeEnum.RSA.getCode()
            );

            log.info("查询回调通知地址返回参数:{}", response);
            JSONObject resp = JSONUtil.parseObj(response.getBody());
            if (response.getStatus() == WxPayExtConstant.OK) {
                String url = resp.getStr("notify_url");
                if(StrUtil.isNotEmpty(url)){
                    return CommonResult.success(url);
                }
            }else{
                channelComplaintOrderContext.setProcessStatus(Boolean.FALSE);
                channelComplaintOrderContext.setRemark(resp.getStr("message"));
            }

        }catch (Exception e) {
            e.printStackTrace();
            log.error("查询回调通知请求失败 message:{}", e.getMessage(), e);
            channelComplaintOrderContext.setRemark("查询回调通知地址请求失败,失败信息" + e.getMessage());
            channelComplaintOrderContext.setProcessStatus(Boolean.FALSE);
        }
        return CommonResult.success();
    }

    @Override
    public CommonResult<String> saveRiskNotifyUrl(ChannelRiskOrderContext channelRiskOrderContext) {
        OrderComplaintChannelDO orderComplaintChannelDO = channelRiskOrderContext.getOrderComplaintChannelDO();
        CommonResult<WxPayConfig> wxPayConfigCommonResult = wxPayCommonParamsHandle.wxComplaintConfig(orderComplaintChannelDO,
                channelRiskOrderContext.isTest());
        if (!wxPayConfigCommonResult.isSuccess()) {
            channelRiskOrderContext.setRemark(wxPayConfigCommonResult.getMsg());
            channelRiskOrderContext.setProcessStatus(false);
        }
        WxPayConfig wxPayConfig = wxPayConfigCommonResult.getData();

        try {
            // 判断是否更新
            RequestMethodEnum requestMethodEnum = RequestMethodEnum.POST;
            if(orderComplaintChannelDO.getRiskFlag() != null && orderComplaintChannelDO.getRiskFlag() == 1){
                requestMethodEnum = RequestMethodEnum.PUT;
            }

            Map<String,String> params = new HashMap<>();
            params.put("notify_url",channelRiskOrderContext.getRiskNotifyUrl());

            IJPayHttpResponse response = WxPayApi.v3(
                    requestMethodEnum,
                    WxDomainEnum.CHINA.toString(),
                    "/v3/merchant-risk-manage/violation-notifications",
                    wxPayConfig.getMchId(),
                    wxPayConfig.getSerialNo(),
                    wxPayConfig.getPayPublicId(),
                    wxPayConfig.getPrivateKeyPath(),
                    JSONUtil.toJsonStr(params),
                    AuthTypeEnum.RSA.getCode()
            );

            log.info("更新风险回调通知地址返回参数:{}", response);
            // 微信支付公钥验证签名
            boolean verifySignature = WxPayKit.verifyPublicKeySignature(response, wxPayConfig.getPayPublicPath());
            if(verifySignature){
                JSONObject resp = JSONUtil.parseObj(response.getBody());
                if (response.getStatus() == WxPayExtConstant.OK) {
                    String url = resp.getStr("notify_url");
                    if(StrUtil.isNotEmpty(url)){
                        return CommonResult.success(url);
                    }
                }else{
                    channelRiskOrderContext.setProcessStatus(Boolean.FALSE);
                    channelRiskOrderContext.setRemark(resp.getStr("message"));
                }
            }else{
                channelRiskOrderContext.setProcessStatus(Boolean.FALSE);
                channelRiskOrderContext.setRemark("更新回调通知地址支付公钥验签失败, " + wxPayConfig.getPayPublicPath());
            }


        }catch (Exception e) {
            log.error("更新回调通知请求失败 message:{}",  e);
            channelRiskOrderContext.setRemark("更新回调通知地址请求失败,失败信息" + e.getMessage());
            channelRiskOrderContext.setProcessStatus(Boolean.FALSE);
        }
        return CommonResult.success();
    }




    @Override
    public String callBackResponse(CommonResult commonResult) {
        if (commonResult.isSuccess()) {
            return "SUCCESS";
        }
        else {
            return "FAIL";
        }
    }


    private WxComplaintDetailResp.ComplaintOrderInfo getOutTradeOrderInfo(List<WxComplaintDetailResp.ComplaintOrderInfo> list){

        if(CollUtil.isEmpty(list)){
            return null;
        }
        return list.get(0);
    }

    /**
     * 微信media转可预览的url
     * @param id     logId
     * @param media  微信媒体资源信息
     * @param wxPayConfig
     * @return
     */
    private OrderComplaintMediaDto getComplaintMedia(String id,ComplaintMediaDto media,WxPayConfig wxPayConfig){

        if(media == null){
            return null;
        }
        OrderComplaintMediaDto complaintMediaDto = new OrderComplaintMediaDto();
        complaintMediaDto.setMediaType(media.getMedia_type());
        complaintMediaDto.setMediaId(wxPayCommonParamsHandle.getImgUrls(id,media.getMedia_url(),null));
        complaintMediaDto.setUrl(wxPayCommonParamsHandle.getImgUrls(id,media.getMedia_url(),wxPayConfig));
        complaintMediaDto.setId(id);
        return complaintMediaDto;
    }
    private List<OrderComplaintMediaDto> getComplaintMedias(String id,List<ComplaintMediaDto> medias,WxPayConfig wxPayConfig){

        List<OrderComplaintMediaDto> mediaList = new ArrayList<>();
        if(CollUtil.isEmpty(medias)){
            return mediaList;
        }
        for(ComplaintMediaDto media:medias){
            mediaList.add(getComplaintMedia(id,media,wxPayConfig));
        }
        return mediaList;
    }

    private List<String> getImgMedias(List<String> imgs,WxPayConfig wxPayConfig){
        List<String> medias = new ArrayList<>();
        String media = null;
        for(String img:imgs){
            media = wxPayCommonParamsHandle.getV3UploadFileMediaId(img,wxPayConfig,ComplaintsApiEnum.IMAGES_UPLOAD.toString());
            if(StrUtil.isNotEmpty(media)){
                medias.add(media);
            }
        }
        return medias;
    }

    /**
     * 微信风险详情本地序列化
     * @param wxComplaintDetailResp  微信风险详情
     * @param wxPayConfig            渠道配置
     * @return
     * @throws Exception
     */
    public OrderComplaintDO formatDetailResp(WxComplaintDetailResp wxComplaintDetailResp,WxPayConfig wxPayConfig) throws Exception {

        // 获取私钥
        PrivateKey privateKey = PayKit.getPrivateKey(wxPayConfig.getPrivateKeyPath(), AuthTypeEnum.RSA.getCode());

        OrderComplaintDO orderComplaintDO = new OrderComplaintDO();
        if(StrUtil.isNotEmpty(wxComplaintDetailResp.getPayer_phone())){
            orderComplaintDO.setPhone(PayKit.rsaDecryptOAEP(wxComplaintDetailResp.getPayer_phone(),privateKey));
        }
        orderComplaintDO.setDetail(wxComplaintDetailResp.getComplaint_detail());
        orderComplaintDO.setCreateTime(DateUtils.shanghaiDate(wxComplaintDetailResp.getComplaint_time(), DateTimeFormatter.ISO_OFFSET_DATE_TIME));
        orderComplaintDO.setAppId(wxComplaintDetailResp.getComplainted_mchid());
        orderComplaintDO.setComplaintId(wxComplaintDetailResp.getComplaint_id());

        WxComplaintDetailResp.ComplaintOrderInfo orderInfo = getOutTradeOrderInfo(wxComplaintDetailResp.getComplaint_order_info());
        if(orderInfo != null){
            orderComplaintDO.setWxOrderNo(orderInfo.getTransaction_id());
            orderComplaintDO.setMchOrderNo(orderInfo.getOut_trade_no());
            orderComplaintDO.setAmount(MerchantUtils.fenToYuan(orderInfo.getAmount()));
        }
        orderComplaintDO.setStatus(ComplaintStatusType.getCode(wxComplaintDetailResp.getComplaint_state()));
        orderComplaintDO.setProblemDesc(wxComplaintDetailResp.getProblem_description());
        orderComplaintDO.setProblemType( wxPayCommonParamsHandle.problemType(wxComplaintDetailResp.getProblem_type()));
        orderComplaintDO.setRefundAmount(MerchantUtils.fenToYuan(wxComplaintDetailResp.getApply_refund_amount()));
        orderComplaintDO.setNum(wxComplaintDetailResp.getUser_complaint_times());
        orderComplaintDO.setPlatform(wxPayCommonParamsHandle.foramtBoolean(wxComplaintDetailResp.getIn_platform_service()));
        orderComplaintDO.setResponse(wxPayCommonParamsHandle.foramtBoolean(wxComplaintDetailResp.getIncoming_user_response()));
        orderComplaintDO.setRefunded(wxPayCommonParamsHandle.foramtBoolean(wxComplaintDetailResp.getComplaint_full_refunded()));

        // 投诉图片
        List<OrderComplaintMediaDto> medias = getComplaintMedias(wxComplaintDetailResp.getComplaint_id(),wxComplaintDetailResp.getComplaint_media_list(),wxPayConfig) ;
        if(CollUtil.isNotEmpty(medias)){
            orderComplaintDO.setComplaintMedias(medias);
            orderComplaintDO.setComplaintMediaJson(JSONUtil.toJsonStr(medias));
        }
        // 获取订单SN
        PayOrderDO payOrderDO = payOrderService.queryWxAssociatedOrder(orderComplaintDO.getWxOrderNo());
        if(payOrderDO != null){
            orderComplaintDO.setSn(payOrderDO.getSn());
            orderComplaintDO.setPayOrderNo(payOrderDO.getPayOrderNo());
            orderComplaintDO.setChannelCode(payOrderDO.getChannelCode());
            orderComplaintDO.setProviderCode(payOrderDO.getProviderCode());
        }
        return orderComplaintDO;
    }



}
